Security Practices
HTTPS Everywhere
- HTTPS (Hypertext Transfer Protocol Secure) = HTTP + TLS (Transport Layer Security).
- Ensures that all communication between the client (browser, mobile app, or service) and the API server is:
- Encrypted → prevents eavesdropping (man-in-the-middle attacks).
- Authenticated → guarantees the server is who it claims to be.
- Integrity-protected → prevents data tampering in transit.
“HTTPS Everywhere” means: Always enforce HTTPS for all REST API requests — no exceptions.
Why HTTPS is Critical for REST APIs
- Protects Sensitive Data
- API calls often include tokens, API keys, passwords, user info.
- Without HTTPS, this data can be stolen in plain text.
- Prevents Man-in-the-Middle (MITM) Attacks
- Attackers can intercept and modify requests on unsecured HTTP.
- With HTTPS, TLS ensures encryption + server authenticity.
- Secures Authentication & Authorization
- Mechanisms like Basic Auth, OAuth 2.0, JWT are safe only if transported via HTTPS.
- Builds Trust
- Most modern clients reject HTTP APIs or warn users.
- Browsers block mixed content (HTTPS site calling HTTP API).
How to Enforce HTTPS in REST APIs
- Redirect all HTTP requests → HTTPS
- If a client mistakenly calls
http://api.example.com, the server responds with301 Moved Permanently→ redirects tohttps://api.example.com.
- Use HSTS (HTTP Strict Transport Security)
- Tells browsers: “Always use HTTPS, even if the user types HTTP.”
Example header:
Strict-Transport-Security: max-age=31536000; includeSubDomains
- Disable HTTP completely
- Block port
80(HTTP) and only accept443(HTTPS).
- Use TLS Certificates (SSL)
- Get a trusted TLS cert (e.g., from Let’s Encrypt).
- Keep certificates updated & rotated.
- Validate Certificates
- Clients should reject self-signed or invalid certs unless explicitly trusted.
REST API over HTTPS
Insecure HTTP Request
GET http://api.example.com/user/profile
Authorization: Bearer abc123token
Risk: The token is sent in plain text. Anyone sniffing the network (Wi-Fi hotspot, ISP, proxy) can steal it.
Secure HTTPS Request
GET https://api.example.com/user/profile
Authorization: Bearer abc123token
The request and token are encrypted with TLS.
Even if intercepted, attackers cannot read or modify it.
Cross-Origin Resource Sharing
- CORS is a browser security mechanism that controls how web applications (running in one origin/domain) can request resources from another origin.
- It prevents malicious websites from making unauthorized requests to your REST API on behalf of a user.
Origin = combination of:
scheme + domain + port
https://app.example.com→ originhttps://api.example.com→ different origin
By default, browsers block cross-origin requests for security. CORS defines rules for when to allow them.
Why CORS is Important in REST APIs
- REST APIs are often consumed by frontend apps hosted on different domains.
- Example:
- Frontend app →
https://myapp.com - Backend API →
https://api.myapp.com
- Frontend app →
- Without CORS → browser blocks API calls.
- With properly configured CORS → only trusted domains can call the API.
How CORS Works
- Simple Request (e.g., GET with standard headers):
- Browser sends request directly with an
Originheader. - Server responds with
Access-Control-Allow-Originheader if allowed.
GET /user/profile HTTP/1.1
Host: api.example.com
Origin: https://myapp.com
Response:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://myapp.com
Content-Type: application/json
{ "username": "masum", "email": "masum@example.com" }
- Preflight Request (for non-simple requests, e.g., POST with custom headers):
- Browser sends an OPTIONS request first.
- Server replies with allowed methods, headers, origins.
- If valid → browser sends the actual request.
Example (Preflight request):
OPTIONS /user/profile HTTP/1.1
Host: api.example.com
Origin: https://myapp.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
Response:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Then the browser proceeds with the real POST request.
CORS Headers Explained
-
Access-Control-Allow-Origin → which domains are allowed (e.g.,
https://myapp.comor*). -
Access-Control-Allow-Methods → allowed HTTP methods (
GET,POST,PUT,DELETE). -
Access-Control-Allow-Headers → allowed request headers (
Authorization,Content-Type). -
Access-Control-Allow-Credentials → whether cookies/auth headers are allowed (
true/false). -
Access-Control-Max-Age → how long preflight results are cached (in seconds).
CORS in REST API
Suppose your API is at https://api.example.com and frontend at https://app.example.com.
Correct CORS Response
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
- Only requests from
https://app.example.comare allowed. - API allows specific methods & headers.
- Cookies or JWT tokens can be sent with
withCredentials = true.
Best Practices for CORS in REST APIs
- Never use
*forAccess-Control-Allow-Originif your API requires authentication. - Restrict allowed methods
- Only expose what’s necessary (
GET,POST).
- Restrict allowed headers
- Avoid letting clients send arbitrary headers.
- Enable credentials only when needed
Access-Control-Allow-Credentials: true → only for trusted apps.
- Use short max-age for sensitive APIs
- Prevents browsers from caching bad CORS rules too long.
Rate Limiting
Rate limiting is the process of restricting the number of requests a client can make to an API within a specific timeframe.
- Prevents DoS/DDoS attacks (attackers flooding API with requests).
- Prevents brute force attacks (guessing passwords or tokens).
- Ensures fair usage so one client doesn’t overload the system.
Example:
- Limit each client to 100 requests per minute.
- If a client exceeds → return
429 Too Many Requests.
Example of Rate Limiting
API allows max 5 requests per minute per user.
Request (6th request within 1 minute)
GET /user/profile HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Response
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json
{
"error": "Rate limit exceeded. Try again in 30 seconds."
}
Retry-After: 30 → tells client how long to wait before retrying.
Common Rate Limiting Strategies
- Fixed Window
- Example: 100 requests per minute.
- Simple but can cause bursts at window edges.
- Sliding Window / Rolling Window
- Tracks requests over the last N seconds.
- Smoother enforcement.
- Token Bucket (most popular)
- Each client has a "bucket" of tokens (e.g., 10 tokens).
- Each request consumes a token.
- Tokens refill at a fixed rate.
- Leaky Bucket
- Similar to token bucket but enforces steady outflow (good for throttling).
Best Practices of Rate Limiting
- Different limits for different endpoints
- Example:
/login(lower limit) vs/posts(higher).
- Example:
- Different limits per user / API key / IP
- Send helpful headers
X-RateLimit-Limit→ max allowedX-RateLimit-Remaining→ remaining requestsX-RateLimit-Reset→ reset time
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 5
X-RateLimit-Reset: 1694200320
- Use caching layer (Redis, Memcached) for fast counting.
- Combine with monitoring to detect abuse.
Throttling
Throttling is slowing down or delaying requests when clients exceed certain thresholds.
- Instead of outright blocking, the API may:
- Queue requests.
- Allow fewer requests per second.
- Ensures system stays responsive under heavy load.
Example:
- API allows 10 requests per second.
- If a client sends 20, extra requests are delayed (processed later).
Example of Throttling
Scenario: API allows max 10 requests per second.
If a client sends 15 requests:
- First 10 are processed immediately.
- Next 5 are delayed and spread out over time.
Client won’t see errors, but responses arrive slower.
Rate Limiting vs Throttling
| Feature | Rate Limiting 🚦 | Throttling 🕒 |
|---|---|---|
| Purpose | Restrict maximum requests | Slow down requests |
| Behavior | Block when limit exceeded | Delay or spread requests |
| Example | 100 requests/minute max | 10 requests/sec, others queued |
| Error Code | 429 Too Many Requests | Usually still 200 (delayed) |
Avoiding common vulnerabilities
SQL Injection
- SQL Injection happens when untrusted input is concatenated into a database query.
- Attackers can manipulate queries to read, modify, or delete data.
Vulnerable Example
# Python pseudo-code
username = request.GET["username"]
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query)
# Python pseudo-code
username = request.GET["username"]
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query)
- If attacker sends:
username=admin' OR '1'='1 - Generated query:
SELECT * FROM users WHERE username = 'admin' OR '1'='1';Returns all users instead of just one.
Prevention
-
Use Parameterized Queries / Prepared Statements:
cursor.execute("SELECT * FROM users WHERE username = %s", (username,)) -
Use ORM frameworks (like Django ORM, Hibernate, SQLAlchemy).
-
Validate and sanitize input (ensure type: string, int, etc.).
Cross-Site Scripting
- XSS occurs when malicious JavaScript is injected into your API’s response.
- If your REST API is consumed by a frontend (like React, Angular), stored XSS can allow attackers to steal cookies, tokens, or session info.
Vulnerable Example
API returns unescaped user input:
{
"comment": "<script>alert('Hacked!')</script>"
}
Browser frontend might render this as executable script → attacker code runs.
Prevention
- Escape or sanitize output before sending to clients.
- Use libraries like DOMPurify (JS), OWASP ESAPI (Java).
- Return only JSON, never raw HTML from REST APIs.
{ "comment": "<script>alert('Hacked!')</script>" }
- Set correct headers
Content-Type: application/jsonX-Content-Type-Options: nosniff
- Implement CSP (Content Security Policy) on frontend.
Cross-Site Request Forgery
- CSRF tricks a logged-in user’s browser into making an unintended request to your API.
- Example: If user is logged into a banking app, an attacker could trick them into transferring money using a malicious webpage.
Vulnerable Example
- User is logged in to
bank.com - Attacker hosts a page with:
<form action="https://bank.com/api/transfer" method="POST">
<input type="hidden" name="amount" value="1000" />
<input type="hidden" name="toAccount" value="attacker123" />
</form>
<script>
document.forms[0].submit();
</script>
User unknowingly sends money while visiting attacker’s site.
Prevention
- Use anti-CSRF tokens
- Include random token in forms/requests.
{ "csrf_token": "abc123xyz" }
- Use SameSite cookies
Set-Cookie: session=abc123; SameSite=Strict; Secure- Prevents cookies from being sent in cross-site requests.
-
Require re-authentication for critical actions (e.g., money transfer).
-
Use JWT in headers instead of cookies for stateless APIs (but still ensure proper CORS rules).
Defense-in-Depth Checklist
| Vulnerability | Protection |
|---|---|
| XSS | Output escaping, CSP, Helmet |
| CSRF | Tokens, SameSite cookies |
| SQL Injection | Parameterized queries |
| All | Input validation |
| All | Least privilege DB users |
Real-world Secure Stack (Recommended)
app.use(helmet());
app.use(express.json());
app.use(cors(corsOptions));
app.use(rateLimiter);
API Gateway usage
- An API Gateway is a single entry point for all client requests to your REST APIs.
- Instead of clients calling services directly, they go through the gateway.
- The gateway then routes, filters, or modifies the requests before sending them to backend microservices.
Why Use an API Gateway for Security?
- Centralized authentication & authorization
- Rate limiting & throttling
- Logging & monitoring (audit trails)
- CORS & HTTPS enforcement
- Input validation & threat protection (e.g., blocking SQLi, XSS patterns)
- Acts as a firewall for APIs (protects backend services from direct exposure)
How an API Gateway Works (Flow)
- Client makes request to
https://api.example.com - API Gateway intercepts request → applies policies (auth, rate limit, CORS, etc.)
- If valid → forwards to backend service (e.g.,
user-service,payment-service) - Collects response and returns it to client
Key Security Features of API Gateway
- Authentication & Authorization
- Gateway can check API keys, JWT tokens, OAuth 2.0 tokens before forwarding requests.
- Example: If a request lacks a valid token → rejected at gateway (never reaches backend).
- Rate Limiting & Throttling
- Prevents abuse and DDoS attacks.
- Example: Limit
100 requests/minuteper user.
- Data Protection (HTTPS Everywhere)
- Gateway enforces TLS/SSL.
- Backend services can remain private (internal network only).
- Input Validation / Threat Protection
- Blocks requests with malicious payloads (SQL injection, XSS, etc.).
- Example: API Gateway WAF (Web Application Firewall) integration.
- CORS Handling
- Centralized CORS configuration instead of every microservice handling it.
- Logging & Monitoring
- Logs all requests/responses.
- Helps detect anomalies (e.g., brute force attempts).
Example Scenario with API Gateway
- Frontend app →
https://app.example.com - API Gateway →
https://api.example.com - Backend services (internal):
user-service.localpayment-service.local
Request Flow
GET /users/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Gateway Actions
-
Check authentication → validate JWT.
-
Check rate limit → ensure client hasn’t exceeded quota.
-
Check CORS policy → ensure request origin is allowed.
-
Forward request →
user-service.local/api/profile.
Response from Gateway
HTTP/1.1 200 OK
Content-Type: application/json
{ "id": 101, "username": "masum", "email": "masum@example.com" }
If client exceeded limit:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{ "error": "Rate limit exceeded. Try again later." }